#include "../../include/util/platform.hpp"
#include "../../include/util/cmem.hpp"
#include "../../include/util/utf8.h"
#include <Windows.h>
#include <fstream>
#include <string>


char* LoadFileAsBytes(const char* path)
{
	//fix open wchar file path
	wchar_t* wstr = (wchar_t*)bmalloc(255);
	size_t wstrsize = utf8_to_wchar(path, 0, wstr, 255);
	wstr[wstrsize] = '\0';

	int size = 0;
	struct _stat64 statBuf;
	if (_wstat64(wstr, &statBuf) == 0)
	{
		size = statBuf.st_size;
	}
	else {
		size = 0;
	}

	std::fstream file;
	char* buf = (char*)bmalloc(size);

	file.open(wstr, std::ios::in | std::ios::binary);
	if (!file.is_open())
	{
		return NULL;
	}
	file.read(buf, size);
	file.close();
	bfree(wstr);
	return buf;
}

char* LoadFileAsBytesEX(const char* path, size_t& _size)
{
	//fix open wchar file path
	wchar_t* wstr = (wchar_t*)bmalloc(255);
	size_t wstrsize = utf8_to_wchar(path, 0, wstr, 255);
	wstr[wstrsize] = '\0';

	int size = 0;
	struct _stat64 statBuf;
	if (_wstat64(wstr, &statBuf) == 0)
	{
		size = statBuf.st_size;
	}
	else {
		size = 0;
	}
	_size = size;

	std::fstream file;
	char* buf = (char*)bmalloc(size);

	file.open(wstr, std::ios::in | std::ios::binary);
	if (!file.is_open())
	{
		return NULL;
	}
	file.read(buf, size);
	file.close();
	bfree(wstr);
	return buf;
}

bool IsFileExist(const char* path)
{
	bool re=false;

	wchar_t* wstr = (wchar_t*)bmalloc(255);
	size_t wstrsize = utf8_to_wchar(path, 0, wstr, 255);
	wstr[wstrsize] = '\0';

	int size = 0;
	struct _stat64 statBuf;
	if (_wstat64(wstr, &statBuf) == 0) {
		re = true;
	}
	bfree(wstr);

	return re;
}

bool WriteJsonFile(Json::Value const& root, const char* path)
{
	wchar_t* wstr = (wchar_t*)bmalloc(255);
	size_t wstrsize = utf8_to_wchar(path, 0, wstr, 255);
	wstr[wstrsize] = '\0';

	std::ofstream os;
	os.open(path, std::ios::out | std::ios::trunc);
	if (!os.good()) {
		os.close();
		return false;
	}
	
	Json::StreamWriterBuilder builder;
	std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
	writer->write(root, &os);
	os.close();
	bfree(wstr);
	return true;
}

void* cat_dlopen(const char* path)
{
	std::string dll_name(path);
	wchar_t* wpath;
	wchar_t* wpath_slash;
	HMODULE h_library = NULL;

	if (!path)
		return NULL;

	//std::replace(dll_name.begin(), dll_name.end(), "\\", "/");
	if (!dll_name.find(".dll"))
		dll_name.append(".dll");

	wpath = (wchar_t*)bmalloc(255);
	size_t wstrsize = utf8_to_wchar(path, 0, wpath, 255);
	wpath[wstrsize] = '\0';

	/* to make module dependency issues easier to deal with, allow
	 * dynamically loaded libraries on windows to search for dependent
	 * libraries that are within the library's own directory */
	wpath_slash = wcsrchr(wpath, L'/');
	if (wpath_slash) {
		*wpath_slash = 0;
		SetDllDirectoryW(wpath);
		*wpath_slash = L'/';
	}

	h_library = LoadLibraryW(wpath);
	bfree(wpath);

	if (wpath_slash)
		SetDllDirectoryW(NULL);

	if (!h_library) {
		DWORD error = GetLastError();

		/* don't print error for libraries that aren't meant to be
		 * dynamically linked */
		if (error == ERROR_PROC_NOT_FOUND)
			return NULL;

		char* message = NULL;

		FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS |
			FORMAT_MESSAGE_ALLOCATE_BUFFER,
			NULL, error,
			MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
			(LPSTR)&message, 0, NULL);

		blog(LOG_INFO, "LoadLibrary failed for '%s': %s (%lu)", path,
			message, error);

		if (message)
			LocalFree(message);
	}

	return h_library;
}

void* cat_dlsym(void* module, const char* func)
{
	void* handle;

	handle = (void*)GetProcAddress((HMODULE)module, func);

	return handle;
}

void cat_dlclose(void* module)
{
	FreeLibrary((HMODULE)module);
}

size_t cat_utf8_to_wcs(const char* str, size_t len, wchar_t* dst, size_t dst_size)
{
	size_t in_len;
	size_t out_len;

	if (!str)
		return 0;

	in_len = len ? len : strlen(str);
	out_len = dst ? (dst_size - 1) : utf8_to_wchar(str, in_len, NULL, 0);

	if (dst) {
		if (!dst_size)
			return 0;

		if (out_len)
			out_len =
			utf8_to_wchar(str, in_len, dst, out_len + 1);

		dst[out_len] = 0;
	}

	return out_len;
}
